package com.ruoyi.framework.aspectj;

import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.annotation.EasyCache;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 自定缓存切面
 * 
 * @date 2022/8/21 21:16
 */
@Aspect
@Component
public class EasyCacheAspect {

    @Autowired
    private RedisCache redisCache;

    /**
     * 创建锁对象（只能创建一个，该对象相当于锁，多个线程共用同一把锁）
     */
    private Object obj = new Object();


    @Pointcut("@annotation(com.ruoyi.common.annotation.EasyCache)"
            + "|| @within(com.ruoyi.common.annotation.EasyCache)")
    public void dsPointCut() {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        EasyCache easyCache = AnnotationUtils.findAnnotation(signature.getMethod(), EasyCache.class);
        // 若缓存参数为空，直接返回
        if (easyCache == null || StringUtils.isBlank(easyCache.cacheName()) || (easyCache.cacheParameter() && easyCache.parameterIndex().length == 0)) {
            return point.proceed();
        }
        StringBuilder redisKey = new StringBuilder(easyCache.cacheName());
        if (easyCache.cacheParameter()) {
            int[] ints = easyCache.parameterIndex();
            for (int i : ints) {
                // 将此序号的参数序列号
                if (i != -1 && point.getArgs().length > i && point.getArgs()[i] != null) {
                    Object arg = point.getArgs()[i];
                    redisKey.append(":").append(Md5Utils.hash(arg.toString()));
                }
            }
        }
        int expire = easyCache.expire();
        // 1、根据缓存key查询是否有缓存
        Object cacheObject = redisCache.getCacheObject(redisKey.toString());
        // 2、若不为空则将数据返回
        if (cacheObject != null) {
            return cacheObject;
        }

        synchronized (obj) {
            // 拿到锁先判断一下缓存内有无数据，若有直接返回，解决多个线程竞争一把锁，导致同时查询数据库的情况
            cacheObject = redisCache.getCacheObject(redisKey.toString());
            if (cacheObject != null) {
                return cacheObject;
            }
            // 3、若为空，执行方法，拿到数据，若数据为空，直接返回，不进行缓存 此处加锁
            Object object = null;
            try {
                object = point.proceed();
                if (object != null) {
                    if (object instanceof TableDataInfo) {
                        TableDataInfo tableDataInfo = (TableDataInfo) object;
                        List rows = tableDataInfo.getRows();
                        if (CollUtil.isNotEmpty(rows)) {
                            redisCache.setCacheObject(redisKey.toString(), object, expire, TimeUnit.MINUTES);
                        } else {
                            // 若没有查询到数据，默认3秒内查询到数据还为空，防止缓存击穿
                            redisCache.setCacheObject(redisKey.toString(), object, 3, TimeUnit.SECONDS);
                        }
                    } else if (object instanceof List) {
                        List lists = (List) object;
                        if (CollUtil.isNotEmpty(lists)) {
                            redisCache.setCacheObject(redisKey.toString(), object, expire, TimeUnit.MINUTES);
                        } else {
                            // 若没有查询到数据，默认3秒内查询到数据还为空，防止缓存击穿
                            redisCache.setCacheObject(redisKey.toString(), object, 3, TimeUnit.SECONDS);
                        }
                    } else {
                        redisCache.setCacheObject(redisKey.toString(), object, expire, TimeUnit.MINUTES);
                    }
                }
            } catch (Throwable throwable) {
                System.out.println("发生异常");
            }

            return object;
        }
    }
}
